#include <rtl/print.h>
#include "bootinfo.h"

#define MODULE ""

extern UINT32 _bootinfo;
extern UINT32 _bootinfo_length;

typedef struct _BOOT_ARG
{
    UINT32 Magic;
    BOOT_MODE BootMode;
    UINT32 EFlag;
    UINT32 LogPort;
    UINT32 LogBaudrate;
    UINT8 LogEnable;
    UINT8 PartNum;
    UINT8 Reserved[2];
    UINT32 DramBankNum;
    UINT32 DramBankSize;

} PACKED BOOT_ARG, *PBOOT_ARG;

VOID AgpPrintBootMode(BOOT_MODE Mode)
{
    switch (Mode)
    {
    case NORMAL_BOOT:
        RtlPrintf("[%s] Boot Mode: %s\n", MODULE, "NORMAL");
        break;
    case META_BOOT:
        RtlPrintf("[%s] Boot Mode: %s\n", MODULE, "META");
        break;
    case RECOVERY_BOOT:
        RtlPrintf("[%s] Boot Mode: %s\n", MODULE, "RECOVERY");
        break;
    case SW_REBOOT:
        RtlPrintf("[%s] Boot Mode: %s\n", MODULE, "SW REBOOT");
        break;
    case FACTORY_BOOT:
        RtlPrintf("[%s] Boot Mode: %s\n", MODULE, "FACTORY");
        break;
    case ADVMETA_BOOT:
        RtlPrintf("[%s] Boot Mode: %s\n", MODULE, "ADVMETA");
        break;
    case ATE_FACTORY_BOOT:
        RtlPrintf("[%s] Boot Mode: %s\n", MODULE, "ATE FACTORY");
        break;
    case ALARM_BOOT:
        RtlPrintf("[%s] Boot Mode: %s\n", MODULE, "ALARM");
        break;
    case FASTBOOT:
        RtlPrintf("[%s] Boot Mode: %s\n", MODULE, "FASTBOOT");
        break;
    case DOWNLOAD_BOOT:
        RtlPrintf("[%s] Boot Mode: %s\n", MODULE, "DOWNLOAD");
        break;
    case UNKNOWN_BOOT:
    default:
        RtlPrintf("[%s] Boot Mode: %s\n", MODULE, "UNKNOWN");
        break;
    }
}

VOID AgpPrintBootReason(BOOT_REASON Reason)
{
    switch (Reason)
    {
    case BOOT_REASON_POWER_KEY:
        RtlPrintf("[%s] Boot Reason: %s\n", MODULE, "POWER KEY");
        break;
    case BOOT_REASON_USB:
        RtlPrintf("[%s] Boot Reason: %s\n", MODULE, "USB");
        break;
    case BOOT_REASON_RTC:
        RtlPrintf("[%s] Boot Reason: %s\n", MODULE, "RTC");
        break;
    case BOOT_REASON_WDT:
        RtlPrintf("[%s] Boot Reason: %s\n", MODULE, "WATCHDOG");
        break;
    case BOOT_REASON_WDT_BY_PASS_PWK:
        RtlPrintf("[%s] Boot Reason: %s\n", MODULE, "WATCHDOG (with power key bypass)");
        break;
    case BOOT_REASON_TOOL_BY_PASS_PWK:
        RtlPrintf("[%s] Boot Reason: %s\n", MODULE, "TOOL (wil power key bypass)");
        break;
    case BOOT_REASON_2SEC_REBOOT:
        RtlPrintf("[%s] Boot Reason: %s\n", MODULE, "2SEC REBOOT (?)");
        break;
    case BOOT_REASON_KERNEL_PANIC:
        RtlPrintf("[%s] Boot Reason: %s\n", MODULE, "KERNEL PANIC");
        break;
    case BOOT_REASON_WDT_SW:
        RtlPrintf("[%s] Boot Reason: %s\n", MODULE, "WDT SW (?)");
        break;
    case BOOT_REASON_WDT_HW:
        RtlPrintf("[%s] Boot Reason: %s\n", MODULE, "WDT HW (?)");
        break;
    case BOOT_REASON_UNKNOWN:
        RtlPrintf("[%s] Boot Reason: %s\n", MODULE, "UNKNOWN");
    default:
        break;
    }
}

PATAG_HEADER AgpGetAtag(UINT32 Tag)
{
    PATAG_HEADER Header = (PATAG_HEADER)_bootinfo;
    SIZE Size = _bootinfo_length;
    while (TRUE)
    {
        if ((UINTPTR)Header - (UINTPTR)_bootinfo >= Size)
            break;

        if (Header->Size == 0)
        {
            RtlPrintf("[%s] Ops, tag size is 0 ...\n", MODULE);
            return NULL;
        }

        if (Header->Tag == Tag)
            return Header;

        Header = ((PATAG_HEADER)((UINTPTR)Header + Header->Size * 4));
    }
    return NULL;
}

VOID AgpDumpBootInfoByAtag(PATAG_HEADER _Header, SIZE Size)
{
    INT I;
    PATAG_BOOT_REASON BootReason;
    PATAG_BOOT_MODE BootMode;
    PATAG_META_COM MetaCom;
    PATAG_LOG_COM LogCom;
    PATAG_MEM Mem;
    PATAG_MD_INFO MdInfo;
    PATAG_DA_INFO DaInfo;

    if ((BootReason = (PATAG_BOOT_REASON)AgpGetAtag(ATAG_TYPE_BOOT_REASON)))
        AgpPrintBootReason((BOOT_REASON)BootReason->BootReason);
    if ((BootMode = (PATAG_BOOT_MODE)AgpGetAtag(ATAG_TYPE_BOOT_MODE)) != NULL)
        AgpPrintBootMode((BOOT_MODE)BootMode->BootMode);
    if ((MetaCom = (PATAG_META_COM)AgpGetAtag(ATAG_TYPE_META_COM)) != NULL)
    {
        RtlPrintf("[%s] META COM ID: %d\n", MODULE, MetaCom->MetaComId);
        RtlPrintf("[%s] META COM TYPE: 0x%08x\n", MODULE, MetaCom->MetaComType);
        RtlPrintf("[%s] META COM PORT: 0x%08x\n", MODULE, MetaCom->MetaUartPort);
    }
    if ((LogCom = (PATAG_LOG_COM)AgpGetAtag(ATAG_TYPE_LOG_COM)) != NULL)
    {
        RtlPrintf("[%s] LOG Enable: %d\n", MODULE, LogCom->LogEnable);
        RtlPrintf("[%s] LOG Baud Rate: %d\n", MODULE, LogCom->LogBaudrate);
        RtlPrintf("[%s] LOG Port: 0x%08x\n", MODULE, LogCom->LogPort);
    }
    if ((Mem = (PATAG_MEM)AgpGetAtag(ATAG_TYPE_MEM)) != NULL)
    {
        RtlPrintf("[%s] Number of DRAM Ranks: %d\n", MODULE, Mem->DramRankNum);
        RtlPrintf("[%s] Rank sizes:\n", MODULE);
        for (I = 0; I < 4; I++)
            RtlPrintf("[%s]    0x%x\n", MODULE, Mem->DramRankSize);
        RtlPrintf("[%s] Number of M-Blocks: %d%s\n", MODULE, Mem->MBlockInfo.MBlockNum, Mem->MBlockInfo.MBlockNum > 128 ? " (something wrong)" : "");
        if (Mem->MBlockInfo.MBlockNum > 0)
        {
            RtlPrintf("[%s]    %-20s %-18s %s\n", MODULE, "Start", "End", "Rank");
            for (I = 0; I < MIN(Mem->MBlockInfo.MBlockNum, 128); I++)
                RtlPrintf("[%s]    0x%016llx - 0x%016llx %d\n",
                          MODULE,
                          Mem->MBlockInfo.MBlock[I].Start,
                          Mem->MBlockInfo.MBlock[I].Start + Mem->MBlockInfo.MBlock[I].Size - 1,
                          Mem->MBlockInfo.MBlock[I].Rank);
        }
// TODO: inspect
#if 0
        RtlPrintf("[%s] Number of DRAM Ranks: %d%s\n", MODULE, Mem->DramInfo.RankNum, Mem->DramInfo.RankNum > 4 ? " (something wrong)" : "");
        if (Mem->DramRankNum > 0)
        {
            for (I = 0; I < MIN(Mem->DramInfo.RankNum, 4); I++)
            {
                RtlPrintf("[%s]    0x%016llx - 0x%016llx\n",
                          MODULE,
                          Mem->DramInfo.RankInfo[I].Start,
                          Mem->DramInfo.RankInfo[I].Start + Mem->DramInfo.RankInfo[I].Size - 1);
            }
        }
#endif
        if (Mem->LcaReservedMemory.Size == 0)
            RtlPrintf("[%s] LCA Reserved Memory: NONE\n", MODULE);
        else
            RtlPrintf("[%s] LCA Reserved Memory: 0x%016llx - 0x%016llx\n",
                      MODULE,
                      Mem->LcaReservedMemory.Start,
                      Mem->LcaReservedMemory.Start + Mem->LcaReservedMemory.Size - 1);
        if (Mem->TeeReservedMemory.Size == 0)
            RtlPrintf("[%s] TEE Reserved Memory: NONE\n", MODULE);
        else
            RtlPrintf("[%s] TEE Reserved Memory: 0x%016llx - 0x%016llx\n",
                      MODULE,
                      Mem->TeeReservedMemory.Start,
                      Mem->TeeReservedMemory.Start + Mem->TeeReservedMemory.Size - 1);
    }
    if ((MdInfo = (PATAG_MD_INFO)AgpGetAtag(ATAG_TYPE_MD_INFO)) != NULL)
    {
        RtlPrintf("[%s] MD INFO: [%d; %d; %d; %d]\n", MODULE, MdInfo->MdType[0], MdInfo->MdType[1], MdInfo->MdType[2], MdInfo->MdType[3]);
    }
    if ((DaInfo = (PATAG_DA_INFO)AgpGetAtag(ATAG_TYPE_DA_INFO)) != NULL)
    {
        RtlPrintf("[%s] DA Info:\n", MODULE);
        RtlPrintf("[%s]    Address:        0x%08x\n", MODULE, DaInfo->Address);
        RtlPrintf("[%s]    Arg1:           0x%08x\n", MODULE, DaInfo->Arg1);
        RtlPrintf("[%s]    Arg2:           0x%08x\n", MODULE, DaInfo->Arg2);
        RtlPrintf("[%s]    Size:           0x%x\n", MODULE, DaInfo->Size);
        RtlPrintf("[%s]    Signature Size: 0x%x\n", MODULE, DaInfo->SignatureSize);
    }

    PATAG_HEADER Header = _Header;
    while (TRUE)
    {
        if ((UINTPTR)Header - (UINTPTR)_Header >= Size)
            break;

        if (Header->Size == 0)
        {
            RtlPrintf("[%s] Ops, tag size is 0 ...\n", MODULE);
            break;
        }

        switch (Header->Tag)
        {
        case ATAG_TYPE_BOOT_REASON:
        case ATAG_TYPE_BOOT_MODE:
        case ATAG_TYPE_META_COM:
        case ATAG_TYPE_LOG_COM:
        case ATAG_TYPE_MEM:
        case ATAG_TYPE_MD_INFO:
        case ATAG_TYPE_DA_INFO:
            break;
        default:
            RtlPrintf("[%s] Unknown TAG 0x%08x SIZE %d\n", MODULE, Header->Tag, Header->Size);
            break;
        }

        Header = ((PATAG_HEADER)((UINTPTR)Header + Header->Size * 4));
    }
}

VOID AgpPrintBootInfo()
{
    PBOOT_ARG Bi = (PBOOT_ARG)_bootinfo;

    if (_bootinfo == 0 || _bootinfo_length == 0)
    {
        RtlPrintf("[%s] Bootinfo missing ...\n", MODULE);
        return;
    }

    RtlPrintf("[%s] Bootinfo at 0x%08x size %d\n", MODULE, _bootinfo, _bootinfo_length);
#if 0
    RtlPrintf("[%s] Magic: 0x%08x\n", MODULE, Bi->Magic);
    AgpPrintBootMode(Bi->BootMode);
    RtlPrintf("[%s] EFlag: 0x%08x\n", MODULE, Bi->EFlag);
    RtlPrintf("[%s] Log Port: %d\n", MODULE, Bi->LogPort);
    RtlPrintf("[%s] Log Baud Rate: %d\n", MODULE, Bi->LogBaudrate);
    RtlPrintf("[%s] Log Enable: %d\n", MODULE, Bi->LogEnable);
    RtlPrintf("[%s] Part Num: %d\n", MODULE, Bi->PartNum);
    RtlPrintf("[%s] DRAM Bank Num: %d\n", MODULE, Bi->DramBankNum);
    RtlPrintf("[%s] DRAM Bank Size: 0x%x\n", MODULE, Bi->DramBankSize);
#else
    AgpDumpBootInfoByAtag((PATAG_HEADER)_bootinfo, (SIZE)_bootinfo_length);
#endif
}
